package cn.iocoder.yudao.module.locale.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.locale.controller.admin.tabledata.vo.TableDataSaveReqVO;
import cn.iocoder.yudao.module.locale.controller.app.tabledata.vo.TableDataConditionReqVO;
import cn.iocoder.yudao.module.locale.dal.dataobject.tabledata.TableDataDO;
import cn.iocoder.yudao.module.locale.framework.config.LocaleTableMetadataConfig;
import cn.iocoder.yudao.module.locale.model.LocaleTableMetadata;
import cn.iocoder.yudao.module.locale.service.tabledata.TableDataService;
import cn.iocoder.yudao.module.system.api.translate.ITranslateApi;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Description: 根据配置翻译某些表的字段并存储
 * Author: li
 * Date: 2024/2/23
 */
@Component
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class})})
@Slf4j
public class TranslateInterceptor implements Interceptor {
    @Resource
    private LocaleTableMetadataConfig localeConfig;
    @Resource
    private TableDataService tableDataService;
    @Resource
    private ITranslateApi translateApi;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        final Object result = invocation.proceed();

        interceptInsertAndUpdate(invocation);

        return result;
    }

    private void interceptInsertAndUpdate(Invocation invocation) throws Exception {
        // 参数不足
        if (invocation == null || invocation.getArgs() == null || invocation.getArgs().length <= 1) {
            return;
        }

        MappedStatement ms = null;
        final Object[] args = invocation.getArgs();

        for (Object arg : args) {
            if (arg instanceof MappedStatement) {
                ms = (MappedStatement) arg;
                if (ms == null || ms.getSqlCommandType() == null) {
                    return;
                }

                final SqlCommandType sqlCommandType = ms.getSqlCommandType();
                if (sqlCommandType != SqlCommandType.INSERT && sqlCommandType != SqlCommandType.UPDATE) {
                    return;
                }
            } else if (arg instanceof MapperMethod.ParamMap) {
                Object objectDO = getObjectDO(arg);
                if (objectDO != null) {
                    if (objectDO instanceof List) {
                        // 批量操作
                        final List list = (List) objectDO;
                        for (Object subObj : list) {
                            handleObject(ms, subObj);
                        }
                    } else {
                        handleObject(ms, objectDO);
                    }
                }
            } else {
                handleObject(ms, arg);
            }
        }
    }

    private void handleObject(MappedStatement ms, Object objectDO) {
        if (ms == null || objectDO == null) {
            return;
        }

        if (localeConfig.getMetadata() == null) {
            return;
        }

        final boolean enable = localeConfig.getMetadata().isEnable();
        if (!enable) {
            return;
        }

        // 获取表名
        final String tableName = extractTableName(objectDO);

        // 获取配置
        final LocaleTableMetadata.TableInfo tableInfo = getTableInfo(tableName);

        if (tableInfo != null) {
            // 获取需要翻译的列名
            final Collection<String> columnsToTranslate = tableInfo.getColumnList();

            if (CollectionUtil.isNotEmpty(columnsToTranslate)) {
                // 获取id字段
                final Field tableIdField = getTableIdField(objectDO);

                // 获取id字段值
                final Object tableIdValue = ReflectUtil.getFieldValue(objectDO, tableIdField.getName());
                if (tableIdValue == null) {
                    return;
                }
                final String rowId = tableIdValue.toString();

                final List<String> supportedLanguages = localeConfig.getMetadata().getSupportedLanguageList();
                for (String languageCode : supportedLanguages) {
                    // 查询多语言国际化数据
                    final List<TableDataDO> tableDataDOList = queryLanguageTableData(languageCode, tableName, rowId);

                    // 遍历需要翻译的列
                    for (String columnName : columnsToTranslate) {
                        // 将列名转换为DO实体类的字段名
                        final String fieldName = camelCaseToUnderscore(columnName);

                        // 获取字段值
                        final Object columnValue = ReflectUtil.getFieldValue(objectDO, fieldName);

                        // 判断字段值是否变更
                        final boolean columnValueChanged = isColumnValueChanged(tableDataDOList, columnName, columnValue);
                        if (!columnValueChanged) {
                            log.info("翻译字段-字段值未变更, tableName = {}, columnName = {}, languageCode = {}, columnValue = {}",
                                    tableName, columnName, languageCode, columnValue);
                            continue;
                        }

                        if (columnValue instanceof String && StrUtil.isNotEmpty((CharSequence) columnValue)) {
                            // 调用Google翻译API进行翻译
                            String translatedValue = translate((String) columnValue, languageCode);
                            log.info("翻译字段, tableName = {}, columnName = {}, languageCode = {}, columnValue = {}, translatedValue = {}",
                                    tableName, columnName, languageCode, columnValue, translatedValue);

                            // 存储多语言国际化数据
                            final TableDataDO oldRow = getTableDataByColumnName(tableDataDOList, columnName);
                            createLanguageTableData(columnName, languageCode, tableName, tableIdValue, (String) columnValue, translatedValue, oldRow);
                        }
                    }
                }
            }
        }
    }

    // 判断字段值是否变更
    private boolean isColumnValueChanged(List<TableDataDO> tableDataDOList, String columnName, Object columnValue) {
        if (CollUtil.isEmpty(tableDataDOList)) {
            return true;
        }

        for (TableDataDO tableDataDO : tableDataDOList) {
            if (tableDataDO.getColumnName().equals(columnName)) {
                final String columnRawValue = tableDataDO.getColumnRawValue();
                if (columnRawValue.equals(columnValue)) {
                    return false;
                }
            }
        }

        return true;
    }

    // 判断字段值是否变更
    private TableDataDO getTableDataByColumnName(List<TableDataDO> tableDataDOList, String columnName) {
        if (CollUtil.isEmpty(tableDataDOList)) {
            return null;
        }

        for (TableDataDO tableDataDO : tableDataDOList) {
            if (tableDataDO.getColumnName().equals(columnName)) {
                return tableDataDO;
            }
        }

        return null;
    }

    // 查询多语言国际化数据
    private List<TableDataDO> queryLanguageTableData(String languageCode, String tableName, String rowId) {
        // 查询是否已存在
        final TableDataConditionReqVO conditionVO = new TableDataConditionReqVO();
        conditionVO.setLanguage(languageCode);
        final TableDataConditionReqVO.TableCondition tableCondition = new TableDataConditionReqVO.TableCondition(tableName, Arrays.asList(rowId));
        conditionVO.setTableConditionList(Arrays.asList(tableCondition));
        final List<TableDataDO> tableDataDOList = tableDataService.selectList(conditionVO);
        return tableDataDOList;
    }

    // 存储多语言国际化数据
    private void createLanguageTableData(String columnName, String languageCode,
                                         String tableName, Object tableIdValue,
                                         String columnValue, String translatedValue,
                                         TableDataDO oldRow) {

        final String rowId = tableIdValue.toString();

        final TableDataSaveReqVO vo = new TableDataSaveReqVO();
        vo.setLanguageCode(languageCode);
        vo.setTableName(tableName);
        vo.setRowId(rowId);
        vo.setColumnName(columnName);
        vo.setColumnRawValue(columnValue);
        vo.setColumnTranslatedValue(translatedValue);
        vo.setStatus(CommonStatusEnum.ENABLE.getStatus());

        if (oldRow == null) {
            tableDataService.createTableData(vo);
        } else {
            vo.setId(oldRow.getId());
            tableDataService.updateTableData(vo);
        }
    }

    private static Field getTableIdField(Object objectDO) {
        final Field[] fields = ReflectUtil.getFields(objectDO.getClass(), new Filter<Field>() {
            @Override
            public boolean accept(Field field) {
                final TableId annotation = field.getAnnotation(TableId.class);
                return annotation != null;
            }
        });

        if (fields != null && fields.length > 0) {
            return fields[0];
        }

        return null;
    }

    // 将驼峰结构的字段名转换为下划线结构的mysql列名
    public static String camelCaseToUnderscore(String camelCase) {
        // 使用正则表达式将驼峰命名转换为下划线命名
        String regex = "([a-z])([A-Z]+)";
        String replacement = "$1_$2";
        String underscore = camelCase.replaceAll(regex, replacement).toLowerCase();
        return underscore;
    }

    private LocaleTableMetadata.TableInfo getTableInfo(String tableName) {
        if (StrUtil.isEmpty(tableName)) {
            return null;
        }

        if (localeConfig.getMetadata() == null) {
            return null;
        }

        for (LocaleTableMetadata.TableInfo info : localeConfig.getMetadata().getTableList()) {
            if (tableName.equals(info.getTableName())) {
                return info;
            }
        }
        return null;
    }

    private Object getObjectDO(Object parameter) {
        final MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameter;
        Object objectDO = null;
        if (map.containsKey("list")) {
            objectDO = map.get("list");
        }
        if (objectDO == null && map.containsKey("param1")) {
            objectDO = map.get("param1");
        }
        return objectDO;
    }

    private String extractTableName(Object objectDO) {
        final TableName tableNameAnno = objectDO.getClass().getAnnotation(TableName.class);
        if (tableNameAnno != null) {
            final String tableName = tableNameAnno.value();
            return tableName;
        }
        return null;
    }

    private String translate(String text, String targetLanguage) {
        final String translatedText = translateApi.translate(text, targetLanguage);
        return translatedText;
    }

}
